
	var SmuoiBase = Class.create({
		
		defineGettersAndSetters:function(){
			for(var k in this){
				if(k.substr(0, 1) !== '$' || !Object.isFunction(this[k])) continue;
				var ft = k.substr(0, 5);
				if(ft == '$get_') this.__defineGetter__(k.substr(5), this[k]);
				else if(ft == '$set_') this.__defineSetter__(k.substr(5), this[k]);
			}
		},
		
		initialize:function(json){
			this.defineGettersAndSetters();	
			Object.extend(this, json);
		}
							 
	});

	var FmgrFile = Class.create(SmuoiBase, {
		getters:['modpath'],
		initialize:function($super, json){
			var o = {};
			for(var k in json) this.getters.include(k) ? o['_'+k] = json[k] : o[k] = json[k];
			$super(o);
		},						
		
		class_is:function(c){
			return this['class'] == c;
		},
		
		is_app:function(){
			return ['lnk','mnt','app','Docroot','Home'].include(this['class'])
		},
		
		is_hidden:function(){
			return (this.basename.substr(0, 1) == '.');
		},
		
		$get_modpath:function(){
			return (!this._modpath || this.is_app()) ? 'Fmgr/0v0r01' : this._modpath;
		},
		
		$get_overlay:function(){
			if(is_null(this._overlay)){
				switch(this['class']){
					case 'lnk':
						this._overlay = 'linked.png'
						break;
					case 'mnt':
						this._overlay = 'mounted.png';
				}
				this._overlay = this._overlay ? Chrome.app_icon(this, 32, '_overlay') : '';
			}
			return this._overlay;
		},
		
		$get_readable:function(){
			return this.perms[0];	
		},
		
		$get_writable:function(){
			return this.perms[1];	
		},
		
		$get_executable:function(){
			return this.perms[2];	
		}
	});

	Smernel.observe('chrome:loaded', function(){											   
		Object.extend(Apps, {
			filter:function(apps){
				var a = {}
				for(var k in apps){
					var f = new FmgrFile(apps[k]);
					if(!f.$get_readable() || (!Dir.showdots && f.is_hidden())) continue;
					a[k] = f;
				}
				return a;
			},
			clickHandler:function(){
				if(Dir['class'] == 'Trash') return moreInfo();
				Loader.open(this);	
			},
			redraw:function(){
				this.load(Dir.contents)
				MC.redraw();	
			},
			makeTR:function(iw, f, i){
				if(!iw.table.trs[0]) this.mkHeadTr(iw);
				var TR = iw.table.tr(i+1, {height:36, className:'DskTr'});
				TR.td(0, {width:32}).style.backgroundImage = "url(" + f.icon + ")";
				if(f.overlay) TR.td(0).mk('div', {}, {width:'32px', height:'32px', background:"url(" + f.overlay + ")"});
				TR.td(1).inp = TR.td(1).mk('input', {type:'text', value:f.name, className:'DskTrInp', readOnly:true, tr:TR})
				Event.observe(TR.td(1), 'click', function(e){
					if(!this.tr.app.perms[0] || !this.tr.selected()) return;
					if(this.locked) return Event.stop(e);
					if(this.tr.ct+600 > new Date().getTime()) return;
					this.locked = true;
					this.inp.readOnly = false;
					this.inp.value = this.tr.app.basename
					this.inp.className = 'DskTrInp-active'
					MC.blur(this.tr.app);
					Event.stop(e)
				});
				Event.observe(TR.td(1).inp, 'blur', function(e){
					this.readOnly = true;
					this.value = this.tr.app.name
					this.tr.td(1).locked = false;
					this.className = 'DskTrInp'
				});
				Event.observe(TR.td(1).inp, 'change', function(e){
					if(this.readOnly || this.value == this.tr.app.basename) return;
					Smajax.exec('renameFile', {id:this.tr.app.id, basename:this.value, refresh:true});
				});
				TR.td(2).innerHTML = bytesize(f.filesize)
				TR.td(3).innerHTML = f.mime
				TR.td(4).innerHTML = UnixToDate(f.updated);
				return TR
			},
			mkHeadTr:function(iw){
				var TR = iw.table.tr(0, {height:20, className:'DskSortTr'});
				//TR.style.height = '10px';
				TR.td(0).member = 'icon';
				TR.td(1, {member:'basename', innerHTML:'Name'});
				TR.td(2, {member:'filesize', innerHTML:'Size'});
				TR.td(3, {member:'mime', innerHTML:'Content-type'});
				TR.td(4, {member:'updated', innerHTML:'Last Modified'});
				for(i=0; i<5; i++) Event.observe(TR.td(i), 'click', function(){
					var desc = (Apps.sortedBy == this.member);
					Apps.sortByMember(this.member, desc);
					Apps.sortedBy = desc ? '' : this.member;
					MC.redraw();
				})
			}
		});
		
		// Get the MC ready
		Apps.load({});
		Chrome.mkMCFrame();
		
		// Smajax does the navigating for us
		Smajax.build_uri = spath;
		Smernel.observe('Smajax:cancel', function(){
			var lgu = Loader.last_good_upath;
			window.location.href = (lgu == '/') ? '/#' : lgu;	
		});
		
		// Let's go
		Dir.refresh()
		Smernel.observe('loggedin', Dir.refresh);
		
		// Listen for address bar input
		setInterval(function(){		
			if(spath() == Loader.spath) return;
			Loader.goto();
		}, 200);
		
		// 	CLIPBOARD =========
		Smernel.observe('Clipboard:written', function(){
			$('clipboard_count').innerHTML = Clipboard.length + ((Clipboard.action == 'cut') ? ' cut ' : ' copied ');
			if(Clipboard.length) $('clipboard_notify').appear()
			else $('clipboard_notify').fade()
		});

	});


// 	LOADER ==========

	// for opera, who returns '#' rather than '' for empty hash, and so was prepending '/'
	var clean_dir_rel_hash = function(h){
		if(!h) return '#';
		if(h != '#' && h.substr(-1) != '/') h += '/';
		return h;
	}
	
	// user path : actual address bar location
	function upath(){
		return window.location.pathname + window.location.hash;
	}
	
	// server path : relative document location
	function spath(){
		return '/' + smuoi_unexplode(window.location.pathname + window.location.hash.substr(1));
	}

	var Loader = {
		_CACHE:{},
		_PATHS:{},
		last_good_upath:'/',
		cache:function(o){
			this._CACHE[o.id] = o;
			this._PATHS[spath()] = o.id;
			this.last_good_upath = upath();
		},
		uncache:function(id){
			this._CACHE[id] = false
		},
		cache_contents:function(id, c){
			this._CACHE[id]['contents'] = c;
		},
		get_contents:function(id){
			return this._CACHE[id]['contents']
		},
		goto:function(loc){
			if(loc) window.location.hash = loc;
			this.spath = spath();
			var id = this._PATHS[this.spath];
			if(id && this._CACHE[id]) return Dir.load(this._CACHE[id]);
			Smajax.exec('dir_open');
		},
		get:function(rel){
			var loc = clean_dir_rel_hash(window.location.hash) + rel + '/' ;
			this.goto(loc);
		},
		// this seems to be Apps.clickHandler
		open:function(f){
			//if(f['class'] == 'file') return moreInfo();
			if(f['class'] == 'lnk') return goTo(f.deskpath);
			if(f.is_app() && !f.executable) return moreInfo();
			if(f.dir){
				// You can view the same directory from more than one address
				// So stick the spath in the _PATHS object and get the location
				if(this._CACHE[f.id]) this._PATHS[this.getspath(f.rel)] = f.id;
				return this.get(f.rel);
			}
			sp = spath()
			if(sp.substr(-1) != '/') sp += '/';
			window.location.href = sp + f.rel;
		},
		getspath:function(rel){
			return spath() + rel + '/' ;	
		},
		// purge is also load()
		purge:function(){
			this.spath = spath();
			this._CACHE = {}
			this._PATHS = {}	
		}
	}
	
// 	DIRECTORY ==========

	var O_ID = '';

	var Dir = {
		dirs:0,
		files:0,
		uploading:false,
		showdots:true,
		contents:{},
		refresh:function(){
			Loader.purge();
			Smajax.exec('dir_open');	
		},
		load:function(o){
			for(var k in o) this[k] = o[k];
			this.writeable = this.perms[1];
			this.loaded();
			Loader.cache(o);
			if(o.contents) this.write_contents();
		},
		loaded:function(){
			$('icontop').style.backgroundImage = 'url(' + Chrome.app_icon({modpath:'Fmgr/0v0r01', icon:this.icon}, 128) + ')';
			$('headloc').innerHTML = this.navigation;
			$('headTit').innerHTML= this.fmrp ;
			O_ID = this.id;
			getcwd = function(){
				return Dir.fmrp;	
			}
		},
		load_contents:function(o){
			this.contents = o;
			this.write_contents();
		},
		append:function(o, id){
			if(!id) id = this.id;
			var c = Loader.get_contents(id)
			for(var k in o) c[k] = o[k]
			if(id == this.id) this.write_contents();
		},
		write_contents:function(){
			Loader.cache_contents(this.id, this.contents)
			this.read();
			Apps.redraw();
			// God only knows why, this is the only way to empty Selection!
			// also, setTimeout(MC.blur.bind(MC), 1); sends integer as first argument to MC.blur, which makes a mess
			setTimeout(function(){ MC.blur(); }, 1); 
			Smernel.fire('Dir:loaded');
		},
		read:function(){
			this.dirs = 0;
			this.files = 0;
			this.filesize = 0;
			var A = this.contents;
			for(var k in A){
				if(A[k].dir) Dir.dirs ++
				else this.files++;
				this.filesize += (A[k].filesize) ? parseInt(A[k].filesize) : 0;
			}
		},
		// Operations
		newDir:function(){
			Smajax.exec('newChild', {id:this.id, type:'dir'});
		},
		newFil:function(){
			Smajax.exec('newChild', {id:this.id, type:'fil'});
		},
		rename:function(){
			var fil = Selection.first;
			TUXprompt('New Filename:', fil.basename, function(nw){
				if(!nw || !nw.trim()) return;	
				Smajax.exec('renameFile', {id:fil.id, basename:nw, refresh:true});
			});
		},
		trash:function(){
			Smajax.exec('trashFiles', {ids:Selection.index, par:this.id, callback:'Selection.clear();'});
		},
		duplicate:function(){
			Smajax.exec('duplicate', {ids:Selection.index, par:this.id});
		},
		upload:function(){
			uploads.clear();
			Browser.new_session();
			var ifh = '<iframe frameborder="0" height="440" width="600" style="border:0; padding:0; margin:0" src="/?fmgr_upload&fmrp=' + this.fmrp + '"></iframe>';
			Browser.load({title:'Upload to  ' + Dir.media_class});
			Browser.receive(ifh)
		},
		download:function(){
			var f = Selected(1);
			if(!f || f.dir) return;
			Smernel.mkSilentIframe('/?force_download='+f.id);
		},
		dotfiles:function(b){
			this.showdots = b;
			Apps.redraw();
		},
		pack:function(){
			var ids = Selection.index;
			Smajax.exec('pack', {ids:ids});	
		},
		unpack:function(){
			Smajax.exec('unpack', {id:Selected(1).id});	
		},
		newlink:function(){
			TUXprompt('Resource path:', Smernel.locality + '::docs:/', function(smrf){
				if(!smrf) return;
				Smajax.exec('newlink', {smrf:smrf});
			}, {footer:'<br /><small>docroot rdk is docs:/, desktop is desk:/</small>'});
		},
		newsym:function(){
			TUXprompt('Real host path:', '/dev/null', function(path){
				if(!path) return;
				Smajax.exec('newsym', {path:path});
			}, {footer:'<br /><small>Path must be full, and can not link to a parent directory of any directory in this domain (due to potential for horrible recursion).'
			+'<br />To reach a file in such a directory, link to it directly.</small>'});
		}
	}

// 	UPLOADS ==========

	var uploads = {
		files:[],
		lastchange:0,
		add:function(f){
			this.files.push(f);
		},
		remove:function(f){
			this.files = this.files.without(f);
			this.lastchange = new Date().getTime();
		},
		clear:function(){
			this.files = [];
			this.lastchange = new Date().getTime();
		},
		lastupdated:function(){
			return ((new Date().getTime()) - this.lastchange)
		}
	}


	// These are all caled by the flash uploader
	function notuploading(f){
		uploads.remove(f);	
	}

	function doneuploading(f){
		uploads.remove(f);
		if((!uploads.files.length)||(uploads.lastupdated() > 30000)){
			uploads.lastchange = new Date().getTime();
			Smajax.exec('get_latest_children');
		}
	}

	function uploading(f, t, p){
		if(p != $C('pubkey').value) return Dir.upload();
		uploads.add(f);
		Smajax.exec('userExpectUpload', {token:t});
	}

	function uploadingto(loc){
		//alert(loc);
	}

// 	SIDEBAR FILEINFO ==========
	
	var ShowFileInfo = function(){
		var l = Selection.length
		$('sel-filecount').style.display = (l) ? 'none' : 'block';
		$('sel-icon').style.display = (l) ? 'block' : 'none';
		$('sel-name').style.display = (l) ? 'block' : 'none';
		switch(l){
			case 0:
				var f = Dir;
				$('sel-class').innerHTML = f.media_class;
				$('sel-fs').innerHTML = bytesize(f.filesize);
				$('sel-name').value = f.name;
				$('sel-filecount').innerHTML = f.dirs + ' director' + ((f.dirs == 1) ? 'y' : 'ies') + '<br />' + f.files + ' file' + ((f.files == 1) ? '' : 's');
				return;
			case 1:
				var f = Selected(1);
				$('sel-icon').style.backgroundImage  = f.el.style.backgroundImage;
				$('sel-class').innerHTML = f.media_class;
				$('sel-fs').innerHTML = bytesize(f.filesize);
				$('sel-name').value = f.name;
				return;
			default:
				var f = Selected();
				$('sel-icon').style.backgroundImage  = "url(" + Chrome.public + '/images/32/multiple.png)';
				$('sel-class').innerHTML = l + ' Items';
				var fs = 0;
				for(var k in f) fs = fs + parseInt(f[k].filesize);
				$('sel-fs').innerHTML = bytesize(fs);
				$('sel-name').value = '(multiple items)';
		}
	}


// 	BROWSER MOREINFO ==========

	var moreInfo = function(d){
		var ids = [], t;
		if(!d && Selection.length) for(var k in Selected()) ids.push(k);
		else ids.push(Dir.id);
		var qs = ids.join(',');
		Browser.new_session();
		if(ids.length > 1) t = 'multiple items';
		else{
			var f = (ids[0] == Dir.id) ? Dir : Selection.first;
			t = f.fmrp + ' (' + f.id + ')';
		}
		Browser.load({title:t, xargs:{ids:ids, query:'moreinfo'}, task:'moreinfo'});
		Browser.goto(spath() + '?' + qs);	
	}
	
	var PermsSet = {
		unames:{},
		gnames:{},
		load:function(files, owner, group){
			this.uid = owner.id;
			this.gid = group.id;
			if(this.uid) this.unames[owner.name] = this.uid
			if(this.gid) this.unames[group.name] = this.gid
			this.files = {};
			this.length = 0;
			this.writeable = true;
			this.dirbit = 0;
			for(var k in files){
				var file = new FileObj(files[k]);
				this.length++;
				if(!file.is_writable) this.writeable = false;
				this.files[k] = file;
				if(file.dir && !this.dirbit) this.dirbit = 1;
			}
			this.multi = (this.length > 1);
			if(!this.writeable) return this.noedits();
			this.initedits();
		},
		noedits:function(){
			$('browse-perms-noedits').appear();
		},
		initedits:function(){
			for(var k in {ownername:'', ownerperms:'', groupname:'', groupperms:'', worldperms:''}){
				Event.observe($(k + '-link'), 'click', this.linkclick);
				Event.observe($(k + '-clear'), 'click', this.clearclick);
				Event.observe($(k), 'click', this.inputclick);
				Event.observe($(k), 'change', this.inputchange);
				Event.observe($('browse-mode'), 'blur',  this.inputchange);
			}
			$('browse-perms-edits').appear();	
		},
		// event funcs
		linkclick:function(e, r){
			var el = e.target;
			var id = el.id.substring(0, el.id.length-5)
			el.hide();
			$(id).show();
			$(id + '-clear').show();
			if(!r && (id.substring(5, 10) == 'perms')) PermsSet.test(id);
		},
		clearclick:function(e, r){
			var el = e.target;
			var id = el.id.substring(0, el.id.length-6)
			el.hide();
			$(id).hide();
			$(id + '-link').show()
			if(!r) PermsSet.test(id);
		},
		inputclick:function(e){
			e.target.select();
		},
		inputchange:function(e){
			PermsSet.test(e.target.id);
		},
		// curElement : select/input or label
		curElement:function(k){
			if($(k).style.display != 'none') return $(k);
			return $(k + '-link');
		},
		// this.test('ownername')
		test:function(o){
			o = this.curElement(o);
			var u = new Array('owner', 'group', 'world');
			// name inputs
			if(o.id.substring(5, 9) == 'name'){
				var n = (o.value) ? o.value.replace(/[^A-Za-z0-9]/, '') : '';
				var cxt = (o.id.substring(0, 5));
				if(!n){
					this.clearclick({target:$(cxt + 'name-clear')}, 1);
					return (cxt ==  'owner') ? this.uid = '' : this.gid = '';
				}
				var namObj = (cxt ==  'owner') ? this.unames : this.gnames;
				if(typeof namObj[n] != 'undefined'){
					var nam = (namObj[n].substr(0, 1) == ':') ? namObj[n].substr(1) : n ;
					var id = namObj[nam];
					o.value = nam;
					o.className= (id) ? 'browse-input-ok' : 'browse-input-notok';
					return (cxt ==  'owner') ? this.uid = id : this.gid = id;
				}
				this.goAjax(cxt, n);
			}
			// perms selectors
			if(o.id.substring(5, 10) == 'perms'){
				var n = this.readPermsValue(o.value);
				var m = $('browse-mode').value.split('');
				var s = '';
				for(var i=0; i<u.length; i++){
					s += (o.id.substring(0, 5) == u[i]) ? n : m[i]
				}
				$('browse-mode').value = s;
				return;
			}
			// mode int box
			if(o.id == 'browse-mode'){
				var m = o.value.substring(0, 3).split('');
				o.value = '';
				for(var i=0; i<u.length; i++){
					var n = ((typeof parseInt(m[i]) != 'NaN') && (m[i] >= 0) && (m[i] <=7)) ? m[i] : '*';
					o.value += n;
					var v = this.readModeNo(n);
					if(!v){
						this.clearclick({target:$(u[i] + 'perms-clear')}, 1);
						continue;	
					}
					$(u[i] + 'perms').value = v;
					this.linkclick({target:$(u[i] + 'perms-link')}, 1);
				}
			}
		},
		// perms evaluator switches
		readPermsValue:function(v){
			var b = this.dirbit;
			switch(v){
				case 'rw':
					return 6 + b;
				case 'ro':
					return 4 + b;
				case 'wo':
					return 2 + b;
				case 'na':
					return 0 + b;
				default:
					return '*';
			}
		},
		readModeNo:function(n){
			switch(true){
				case (n == '*'):
					return false;
				case (n >= 6):
					return 'rw';
				case (n >= 4):
					return 'ro';
				case (n >= 2):
					return 'wo';
				default:
					return 'na';
			}
		},
		goAjax:function(t, n){
			Smajax.exec('findUnames', {type:t, name:n});
		},
		receiveUnames:function(t, n, o){
			var id = '';
			var nam = '';
			for(nam in o){
				id = o[nam]
				break;
			}
			var namObj = (t == 'owner') ? this.unames : this.gnames;
			namObj[n] = (nam) ? ':' + nam : '';
			for(var nam in o) namObj[nam] = o[nam];
			this.test(t + 'name');
		},
		save:function(){
			var args = {}
			args.cxt = Dir.id;
			if(this.uid) args.own = this.uid;
			if(this.gid) args.grp = this.gid;
			args.mod = $('browse-mode').value;
			args.stb = ($('browse-stb').checked) ? 1 : '';
			args.rcr = ($('browse-rcr').checked) ? 1 : '';
			args.ids = []
			for(var k in this.files) args.ids.push(k);
			Smajax.exec('setPermissions', args);
		}
	}
	